﻿
#region Using Directives.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;
using RWXMLEdit.Common;
using ZedGraph;
using DataPoint = System.Windows.Forms.DataVisualization.Charting.DataPoint;

#endregion

namespace RWXMLEdit.Client.Forms
{
    public partial class FormEditSettings : Form
    {
        #region Fields.

        private readonly XDocument _xDocument;
        private readonly string _railWorksPath;
        private const string MsgBoxName = "RailWorks XML Editor Message";
        private readonly Dictionary<Guid, CsvInfo> _dataTables = new Dictionary<Guid, CsvInfo>();
        private CsvInfo _currentCsvInfo;
        private string _graphName;

        #endregion

        #region Constructors.

        public FormEditSettings(XDocument xdoc, string railworkspath)
        {
            InitializeComponent();

            Text = "CSV Editor";

            _xDocument = xdoc;
            _railWorksPath = railworkspath;

            Init();
        }

        [Localizable(false)]
        public override sealed string Text
        {
            get { return base.Text; }
            set { base.Text = value; }
        }

        #endregion

        #region Events.

        private void cboCsvFiles_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(cboCsvFiles.SelectedIndex == 0)
            {
                _currentCsvInfo = null;

                ResetGraph(zedGraph);
                
                dgvCsv.DataSource = null;
            }
            else
            {
                if (_currentCsvInfo != null && _dataTables.ContainsKey(_currentCsvInfo.Id))
                {
                    _dataTables[_currentCsvInfo.Id] = _currentCsvInfo;
                }

                CsvInfo temp = (CsvInfo)cboCsvFiles.SelectedItem;

                if (_dataTables.ContainsKey(temp.Id))
                {
                    _dataTables.TryGetValue(temp.Id, out _currentCsvInfo);
                }
                else
                {
                    _currentCsvInfo = temp;

                    if (!_currentCsvInfo.HasPath)
                    {
                        string path = CreateCsvFile(_currentCsvInfo.Text);

                        if (string.IsNullOrEmpty(path))
                        {
                            return;
                        }

                        _currentCsvInfo.Path = path;
                        _currentCsvInfo.HasPath = true;
                    }
                    try
                    {
                        _currentCsvInfo.Table = CsvHelper.OpenCsvFile(_currentCsvInfo.Path);

                        _dataTables.Add(_currentCsvInfo.Id, _currentCsvInfo);
                    }
                    catch (CsvException ex)
                    {
                        string message = string.Format("There was a problem opening the CSV-file [{0}]\n\n{1}", ex.FileName, ex.Message);

                        MessageBox.Show(this, message, MsgBoxName, MessageBoxButtons.OK, MessageBoxIcon.Error);

                        return;
                    }
                }

                dgvCsv.DataSource = _currentCsvInfo.Table;

                dgvCsv.Columns[0].SortMode = DataGridViewColumnSortMode.NotSortable;
                dgvCsv.Columns[1].SortMode = DataGridViewColumnSortMode.NotSortable;

                dgvCsv.Sort(dgvCsv.Columns[0], ListSortDirection.Ascending);

                _graphName = string.Format("Graph for {0}", Path.GetFileName(_currentCsvInfo.Path));

                InitNewChart(_currentCsvInfo.Table, _currentCsvInfo.XData, _currentCsvInfo.YData, _graphName);
            }
        }

        private void dgvCsv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            InitNewChart(_currentCsvInfo.Table, _currentCsvInfo.XData, _currentCsvInfo.YData, _graphName);
        }

        private void dgvCsv_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            if(cboCsvFiles.SelectedIndex > 0)
                InitNewChart(_currentCsvInfo.Table, _currentCsvInfo.XData, _currentCsvInfo.YData, _graphName);
        }

        private void dgvCsv_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            if (e.ListChangedType != ListChangedType.ItemAdded)
                InitNewChart(_currentCsvInfo.Table, _currentCsvInfo.XData, _currentCsvInfo.YData, _graphName);
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            foreach (KeyValuePair<Guid, CsvInfo> ci in _dataTables)
            {
                try
                {
                    CsvHelper.SaveCsvFile(ci.Value.Table, false, false, ci.Value.Path);
                }
                catch(CsvException ex)
                {
                    string message = string.Format("There was a problem saving the CSV-file [{0}]\n\n{1}", ex.FileName, ex.Message);
                    MessageBox.Show(this, message, MsgBoxName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        #endregion

        #region Private Methods.

        private void Init()
        {
            InitComboBox();
        }

        private void ResetGraph(ZedGraphControl control)
        {
            GraphPane gp = control.GraphPane;

            gp.CurveList.Clear();
            gp.GraphObjList.Clear();

            gp.Title.Text = "CSV Plotter";
            gp.XAxis.Title.Text = "X";
            gp.YAxis.Title.Text = "Y";
            gp.XAxis.MajorGrid.IsVisible = true;
            gp.YAxis.MajorGrid.IsVisible = true;
            gp.XAxis.MajorGrid.Color = Color.LightGray;
            gp.YAxis.MajorGrid.Color = Color.LightGray;

            zedGraph.AxisChange();
            zedGraph.Invalidate();
            zedGraph.Refresh();
        }

        private string CreateCsvFile(string name)
        {
            string path = string.Empty;

            string message = string.Format("There no CSV-file for the attribute [{0}]\n\nWould you like to create a new file?", name);

            DialogResult dlgResult = MessageBox.Show(this, message, MsgBoxName, MessageBoxButtons.YesNo, MessageBoxIcon.Question);

            if (dlgResult == DialogResult.Yes)
            {
                saveFileDialog.InitialDirectory = _railWorksPath;
                saveFileDialog.FileName = string.Format("{0}.csv", name);
                saveFileDialog.Title = @"Create CSV File";

                DialogResult save = saveFileDialog.ShowDialog(this);

                if (save == DialogResult.OK)
                {
                    path = saveFileDialog.FileName;

                    TextWriter tw = new StreamWriter(path);

                    tw.WriteLine("0,0");

                    tw.Close();

                    SaveNewPathToXml(path, name);
                }
            }

            return path;
        }

        private void InitComboBox()
        {
            List<CsvInfo> items = GetSimulationCSVFilePath();

            if(items != null)
            {
                CsvInfo item = new CsvInfo("<Select CSV File>");

                cboCsvFiles.Items.AddRange(items.ToArray());

                cboCsvFiles.Items.Insert(0, item);

                cboCsvFiles.SelectedIndex = 0;
            }
            else
            {
                cboCsvFiles.Enabled = false;
                btnOK.Enabled = false;
            }
        }

        private void InitNewChart(DataTable dt, string xtitle, string ytitle, string graphtitle)
        {
            PointPairList list = new PointPairList();

            GraphPane gp = zedGraph.GraphPane;

            gp.CurveList.Clear();
            gp.GraphObjList.Clear();

            DataView dv = dt.DefaultView;

            dv.Sort = "X-Value ASC";

            dt = dv.ToTable();

            gp.Title.Text = graphtitle;
            gp.XAxis.Title.Text = xtitle;
            gp.YAxis.Title.Text = ytitle;
            gp.XAxis.MajorGrid.IsVisible = true;
            gp.YAxis.MajorGrid.IsVisible = true;
            gp.XAxis.MajorGrid.Color = Color.LightGray;
            gp.YAxis.MajorGrid.Color = Color.LightGray; 

            foreach (DataRow row in dt.Rows)
            {
                double x;
                double y;

                double.TryParse(row[0].ToString(), out x);
                double.TryParse(row[1].ToString(), out y);

                PointPair pp = new PointPair(x, y);

                list.Add(pp);
            }

            LineItem curve = new LineItem("Csv plot", list, Color.Tomato, SymbolType.Circle, 2);

            curve.Line.Width = 2.0F;
            curve.Line.IsAntiAlias = true;
            curve.Symbol.Fill = new Fill(Color.Tomato);
            curve.Symbol.Size = 7;
            const double offset = 20.0;

            foreach (PointPair pt in list)
            {
                string coord = string.Format("({0}, {1})",pt.X, pt.Y);
                TextObj text = new TextObj(coord, pt.X, pt.Y + offset, CoordType.AxisXYScale, AlignH.Center, AlignV.Center);
                text.ZOrder = ZOrder.A_InFront;
                text.FontSpec.Border.IsVisible = false;
                text.FontSpec.Fill.IsVisible = false;
                text.FontSpec.IsBold = true;
                gp.GraphObjList.Add(text);
            }
            
            gp.YAxis.Scale.MaxGrace = 0.2;
            gp.CurveList.Add(curve);

            zedGraph.AxisChange();
            zedGraph.Invalidate();
            zedGraph.Refresh();
        }

        #endregion

        #region Get paths to CSV files.

        private string GetSimulationXMLPath(out string csvpath)
        {
            string path = string.Empty;

            csvpath = string.Empty;

            XElement doc = XElement.Load(_xDocument.CreateReader());

            XElement element = (from x in doc.DescendantsAndSelf()
                                where (string)x.Attribute("name") == "EngineSimulationContainer"
                                select x).SingleOrDefault();

            if (element != null)
            {
                string provider = (from x in element.Descendants()
                                   where (string)x.Attribute("name") == "Provider"
                                   select x.Element("cDeltaString").Element("Element").Element("Value").Value).ElementAt(0);

                string product = (from x in element.Descendants()
                                  where (string)x.Attribute("name") == "Product"
                                  select x.Element("cDeltaString").Element("Element").Element("Value").Value).ElementAt(0);

                string blueprint = (from x in element.Descendants()
                                    where (string)x.Attribute("name") == "BlueprintID"
                                    select x.Element("cDeltaString").Element("Element").Element("Value").Value).ElementAt(0);

                path = Path.Combine(_railWorksPath, "Source", provider, product, blueprint);

                csvpath = Path.Combine(_railWorksPath, "Source", provider, product);
            }

            return path;
        }

        private Dictionary<CsvInfo, XElement> CheckSimulationElements(string path)
        {
            Dictionary<CsvInfo, XElement> elements = new Dictionary<CsvInfo, XElement>();

            List<CsvInfo> items = CsvHelper.GetAllAttributes();

            XElement doc = XElement.Load(path);

            foreach (CsvInfo attribute in items)
            {
                XElement element = (from x in doc.DescendantsAndSelf()
                                    where (string)x.Attribute("name") == attribute.Text
                                    select x).SingleOrDefault();

                if (element != null)
                {
                    elements.Add(attribute, element);
                }
            }

            return elements;
        }

        private List<CsvInfo> GetSimulationCSVFilePath()
        {
            List<CsvInfo> items = new List<CsvInfo>();

            string tempPath = string.Empty;

            string path = GetSimulationXMLPath(out tempPath);

            if (!File.Exists(path))
                return null;

            Dictionary<CsvInfo, XElement> elements = CheckSimulationElements(path);

            foreach (KeyValuePair<CsvInfo, XElement> keyValuePair in elements)
            {
                string csvpath = (from x in keyValuePair.Value.Descendants()
                                    where (string)x.Attribute("name") == "CsvFile"
                                    select x.Element("cDeltaString").Element("Element").Element("Value").Value).ElementAt(0);

                if(string.IsNullOrEmpty(csvpath))
                {
                    keyValuePair.Key.Path = string.Empty;
                    keyValuePair.Key.HasPath = false;
                }
                else
                {
                    keyValuePair.Key.Path = Path.Combine(tempPath, csvpath);
                    keyValuePair.Key.HasPath = true;
                }

                items.Add(keyValuePair.Key);
            }

            return items;
        }

        #endregion

        #region Save New CSV File To XML

        private void SaveNewPathToXml(string path, string attribute)
        {
            string csvPath = string.Empty;

            string xmlPath = GetSimulationXMLPath(out csvPath);

            XElement doc = XElement.Load(xmlPath, LoadOptions.PreserveWhitespace);

            XElement element = (from x in doc.DescendantsAndSelf()
                                where (string)x.Attribute("name") == attribute
                                select x).SingleOrDefault();

            if(element != null)
            {
                XElement element1 = (from x in element.Descendants()
                              where (string)x.Attribute("name") == "CsvFile"
                                     let xElement = x.Element("cDeltaString")
                                     where xElement != null
                                     let xElement1 = xElement.Element("Element")
                                     where xElement1 != null
                                     select xElement1.Element("Value")).Single();

                if (path.Contains(csvPath))
                {
                    path = path.Replace(csvPath, "");

                    if(path.StartsWith("\\"))
                    {
                        path = path.Remove(0, 1);
                    }
                }

                element1.Value = path;

                doc.Save(xmlPath, SaveOptions.None);
            }
        }

        #endregion
    }
}
